#import "BWStitchEditCanvas.h"
#import "BWSymbol.h"

#define scalePoint(point, amount, origin)       \
do {                                    \
    point.x *= amount;                  \
    point.y *= amount;                  \
    point.x += origin.x;                \
    point.y += origin.y;                \
} while (0)



@implementation BWStitchEditCanvas

- (void) setRawPath: (BWSymbol *) path;
{
    [path retain];
    [rawPath release];
    rawPath = path;
    
} // setPoints


- (void) awakeFromNib
{
    trackingIndex = -1;
    selectedIndex = -1;
} // awakeFromNib


// the white square in the middle with all the Good Stuff
// and a lot of code assumes that this square is actually square
- (NSRect) liveRect
{
    NSRect rect, bounds;

    bounds = [self bounds];

    float side;
    side = MIN(bounds.size.width, bounds.size.height) / 2;

    float x, y;
    x = (bounds.size.width - side) / 2 ;
    y = (bounds.size.height - side) / 2;

    rect = NSMakeRect (x, y, side, side);

    return (rect);

} // liveRect


- (NSPoint) scaleViewPoint: (NSPoint) point
{
    // take a click and scale it to the 1x1 square based in the middle
    // of the view
    NSRect liveRect;
    liveRect = [self liveRect];
    
    point.x -= liveRect.origin.x;
    point.y -= liveRect.origin.y;
    point.x /= liveRect.size.width;
    point.y /= liveRect.size.width;

    return (point);

} // scaleViewPoint


- (NSPoint) getScaledPointAtIndex: (int) index
{
    NSPoint point;

    // map from 0.0 - 1.0  into the bounds of the view
    NSRect liveRect;
    liveRect = [self liveRect];

    point = [rawPath pointAtIndex: index];
    
    scalePoint (point, liveRect.size.width, liveRect.origin);

    return (point);
    
} // getScaledPointAtIndex


#define BLOB_SIZE 5.0

- (void) drawPoint: (NSPoint) point  selected: (BOOL) selected
{
    if (controlPointToggleState && !draggingPath) {
        NSBezierPath *path = [[NSBezierPath alloc] init];
        
        [path appendBezierPathWithArcWithCenter: point
              radius: BLOB_SIZE / 2.0
              startAngle: 0.0  
              endAngle: 360.0];
        
        if (selected) {
            [[NSColor greenColor] set];
            [path fill];
        } else {
            [[NSColor blackColor] set];
            [path fill];
            [[NSColor redColor] set];
            [path stroke];
        }
    }

} // drawPoint


- (void) drawPoints
{
    NSRect bounds;
    bounds = [self bounds];

    int count;
    count = [rawPath pointCount];

    int i;
    for (i = 0; i < count; i++) {
        NSPoint point;
        point = [self getScaledPointAtIndex: i];

        [self drawPoint: point
            selected: (i == selectedIndex)];
    }

} // drawPoints


- (void) drawInfluenceLines
{
    if (curveToggleState && !draggingPath) {
        NSRect liveRect;
        liveRect = [self liveRect];
        
        [[NSColor redColor] set];
        
        int elementCount;
        elementCount = [rawPath elementCount];
        
        int i;
        for (i = 0; i < elementCount; i++) {
            NSPoint points[3];
            NSBezierPathElement element;
            
            element = [rawPath elementAtIndex: i
                               associatedPoints: points];
            
            if (element == NSCurveToBezierPathElement) {
                NSPoint priorPoints[3], priorPoint;
                // the first element should always be the initial MoveTo,
                // so this should be safe
                element = [rawPath elementAtIndex: i - 1
                                   associatedPoints: priorPoints];
                
                if (element == NSCurveToBezierPathElement) {
                    priorPoint = priorPoints[2];
                } else {
                    priorPoint = priorPoints[0];
                }
                float size = liveRect.size.width;
                
                scalePoint (priorPoint, size, liveRect.origin);
                
                scalePoint (points[0], size, liveRect.origin);
                scalePoint (points[1], size, liveRect.origin);
                scalePoint (points[2], size, liveRect.origin);
                
                [NSBezierPath strokeLineFromPoint: priorPoint
                              toPoint: points[0]];
                [NSBezierPath strokeLineFromPoint: points[0]
                              toPoint: points[1]];
                [NSBezierPath strokeLineFromPoint: points[1]
                              toPoint: points[2]];
            }
        }
    }

} // drawInfluenceLines


- (NSBezierPath *) makePathInRect: (NSRect) rect
{
    if (draggingPath) {
        rect.origin.x += offsetX;
        rect.origin.y += offsetY;
    }

    NSBezierPath *path = [rawPath pathInRect: rect];
    
    return (path);
    
} // makePathInRect




- (void) drawRect: (NSRect) bounds
{
    bounds = [self bounds];

    [[NSColor whiteColor] set];
    [NSBezierPath fillRect: bounds];

    NSRect liveRect;
    liveRect = [self liveRect];
    [[NSColor grayColor] set];
    [NSBezierPath strokeRect: liveRect];

    NSBezierPath *path;
    path = [self makePathInRect: liveRect];

    [[NSColor blueColor] set];
    [path fill];

    NSColor *grayWash;

#define GRAY_VALUE 0.9
    grayWash = [NSColor colorWithCalibratedRed: GRAY_VALUE
                        green: GRAY_VALUE
                        blue: GRAY_VALUE
                        alpha: 0.8];

    path = [NSBezierPath bezierPath];
    [path appendBezierPathWithRect: bounds];
    [path appendBezierPathWithRect: liveRect];
    [grayWash set];
    [path setWindingRule: NSEvenOddWindingRule];
    [path fill];

    // draw after the wash
    [NSBezierPath setDefaultLineWidth: 0.75];
    [self drawInfluenceLines];
    [self drawPoints];

    [[NSColor blackColor] set];
    [NSBezierPath strokeRect: bounds];

} // drawRect


- (void) mouseDown: (NSEvent *) event
{
    NSRect bounds;
    bounds = [self bounds];

    NSPoint click;
    click = [self convertPoint: [event locationInWindow]
                  fromView: nil];

    // figure out the point

    trackingIndex = -1;
    draggingPath = NO;

    int count;
    count = [rawPath pointCount];

    int i;
    for (i = 0; i < count; i++) {
        NSRect rect;
        
        NSPoint point;
        point = [self getScaledPointAtIndex: i];

        rect = NSMakeRect (point.x - BLOB_SIZE / 2,
                           point.y - BLOB_SIZE / 2,
                           BLOB_SIZE, BLOB_SIZE);
        if (NSPointInRect(click, rect)) {
            overridePoint = [rawPath pointAtIndex: i]; // don't move on mouse-down
            trackingIndex = i;
            [rawPath setGhostIndex: i  
                     point: overridePoint];
            break;
        }
    }

    selectedIndex = trackingIndex;
    [delegate selectedPoint: selectedIndex];

    if (trackingIndex == -1) {
        NSRect liveRect;
        liveRect = [self liveRect];

        NSBezierPath *path;
        path = [rawPath pathInRect: liveRect];

        if ([path containsPoint: click]) {
            draggingPath = YES;
            offsetX = 0.0;
            offsetY = 0.0;
            originalClick = click;
        }
    }

    [self setNeedsDisplay: YES];

} // mouseDown


- (void) mouseUp: (NSEvent *) event
{
    // commit point
    if (trackingIndex != -1) {
        // reset the ghost point so that undo will pick up the
        // correct value
        [rawPath setGhostIndex: -1  point: NSZeroPoint];

        [delegate pointIndex: trackingIndex
                  changedToPoint: overridePoint];

        trackingIndex = -1;

        overridePoint = NSZeroPoint;
    }

    if (draggingPath) {
        NSRect liveRect;
        liveRect = [self liveRect];

        offsetX /= liveRect.size.width;
        offsetY /= liveRect.size.height;

        [delegate movePathByX: offsetX  Y: offsetY];
        draggingPath = NO;
    }

    [self setNeedsDisplay: YES];

} // mouseUp


- (void) mouseDragged: (NSEvent *) event
{
    NSPoint click;
    click = [self convertPoint: [event locationInWindow]
                  fromView: nil];
    
    if (trackingIndex != -1) {
        NSRect bounds;
        bounds = [self bounds];
        
        overridePoint = [self scaleViewPoint: click];
        [rawPath setGhostIndex: trackingIndex  point: overridePoint];
        
    }

    if (draggingPath) {
        offsetX = click.x - originalClick.x;
        offsetY = click.y - originalClick.y;
    }

    [self setNeedsDisplay: YES];

} // mouseDragged


- (int) selectedIndex
{
    return (selectedIndex);
} // selectedIndex

- (BOOL) curveToggleState
{
    return (curveToggleState);
} // curveToggleState


- (void) setCurveToggleState: (BOOL) onOff
{
    curveToggleState = onOff;
    [self setNeedsDisplay: YES];

} // setCurveToggleState


- (BOOL) controlPointToggleState
{
    return (curveToggleState);
} // curveToggleState


- (void) setControlPointToggleState: (BOOL) onOff
{
    controlPointToggleState = onOff;
    [self setNeedsDisplay: YES];

} // setControlPointToggleState

- (BOOL) isFlipped
{
    return (YES);
} // isFlipped

@end // BWStitchEditCanvas

